<script>
// function 声明函数 this 随上下文改变（this不固定）
window.x = 100
function fn() {
  console.log(this.x)
}
fn() // 没有上下文，把window当成this
     // 100 (取window.x)
let o1 = {x: 1, fn}
o1.fn() // 上下文是o1, 把o1当成this
        // 1 (取o1.x)
let o2 = {x: 2, fn}
o2.fn() // 上下文是o2, 把o2当成this
        // 2(取o2.x)
let a = {o3: {x:3, fn}}
a.o3.fn() // 上下文是a.o3, 把a.o3当成this
          // 3(取a.o3.x)
// Function.prototype.call/apply/bind
//    的第一个参数是指定函数的this
fn.call(o1) // 让fn把o1当成this用
            // 1 （取o1.x)
// Function.prototype.bind 返回一个新的function，
//    内部的this是固定的
const fn2 = fn.bind(o2) // fn2的 this 是 o2
                        // 并且它的this是固定不变的
fn2() // 看起来没有上下文，但是内部的this 是o2
      // 结果：2 (取o2.x)
let o4 = {x: 4, fn: fn2}
o4.fn() // o4.fn的上下文是o4, 但o4.fn的this是固定的
        // 结果: 2 (取o2.x)
fn2.call(o1) // 虽然call想把o1当成fn2的this
             // 但是fn2的this是固定为o2，所以无效
             // 结果: 2 (取o2.x)

// 箭头函数的this不随上下文改变（this不变）
//   箭头函数只要创建了，它的this就不会改变了
// 箭头函数的this = 它创建时候的环境的this
const fn3 = () => console.log(this.x)
// 当前环境this = window
fn3() // 100 (取window.x)
let o5 = {x: 5, fn: fn3}
o5.fn() // 100 (取window.x)

const fn4 = function () {
  // this = ?
  return () => console.log(this.x)
  return (function() {console.log(this.x)})
    .bind(this) // 和上面一句的效果相同
}
const fn5 = fn4()
// fn4()执行的时候没有指定上下文，所以fn4当时this是window
fn5() // 100 （取window.x)

const o6 = {x: 6, fn: fn4}
const fn6 = o6.fn() // 上下文是o6，this=o6
// 返回的箭头函数生成的时候环境this=o6
fn6() // 6 (取o6.x)

function a() {
  return () => {}
}
console.log(a() === a()) // false

// 容易产生歧义的写法

const o7 = {
  x: 7,
  fn: () => console.log(this.x) // 环境的this=window
}
o7.fn() // 100 （取window.x）

const o8 = {
  x: 8,
  delayX: function() {
    setTimeout(function() { // 调用时无上下文this=window
      console.log(this.x)
    }, 1000)
  },
  delayX2: function() {
    setTimeout(
      () => console.log(this.x),
      // o8.delayX2()时this=o8
      1000
    )
  }
}
o8.delayX() // 100
o8.delayX2() // 8
</script>